/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufba.lasid.v1.jitclouds.mda;

import br.ufba.lasid.v1.jitclouds.mda.api.JiTFTManagerAPI;
import br.ufba.lasid.v1.jitclouds.mda.api.JiTMonitorAPI;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.ICloud;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.ICluster;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.IDataCenter;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.ft.IVMFailoverDeployer;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.virtualization.IHypervisor;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.virtualization.INode;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.virtualization.IVirtualMachine;
import br.ufba.lasid.v1.jitclouds.mda.providers.IProvider;
import java.io.PrintStream;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;

/**
 *
 * @author aliriosa
 */
public final class MDA {
    
    /**
     * Cloud Provider Tag in Configuration file
     */
    public static final String CloudProvider = "mda.cloud.provider";

    /**
     * Define a Meta Tag which identifies a DC
     */
    public static final String CloudTag = "mda.cloud.id";
    
    /**
     * Data Center Provider Tag in Configuration file
     */    
    public static final String DCProvider = "mda.dc.provider";
    
    /**
     * Define a Meta Tag which identifies a DC
     */
    public static final String DCTag = "mda.dc.id";

    /**
     * Cluster Provider Tag in Configuration file
     */        
    public static final String ClusterProvider = "mda.cluster.provider";   
    
    /**
     * Define a Meta Tag which identifies a Cluster
     */
    public static final String ClusterTag = "mda.cluster.id";

    /**
     * Node Provider Tag in Configuration file
     */        
    public static final String NodeProvider = "mda.cluster.node";   
    
    /**
     * Define a Meta Tag which identifies a Node
     */    
    public static final String NodeTag = "mda.node.id";
    
    /**
     * Hypervisor Provider Tag in Configuration file
     */        
    public static final String HypervisorProvider = "mda.hypervisor.provider";

    /**
     * Define a Meta Tag which identifies a Hypervisor
     */        
    public static final String HypervisorTag = "mda.hypervisor.id";
    
    /**
     * Virtual Machine Provider Tag in Configuration file
     */            
    public static final String VMProvider = "mda.vm.provider";
    
    /**
     * Define a Meta Tag which identifies a Virtual Machine
     */    
    public static final String VMTag = "mda.vm.id";
    
    /**
     * Virtual Machine Failover Deployer Provider Tag in Configuration file
     */            
    public static final String VMFailoverDeployerProvider = "mda.vfdeployer.provider";
    
    /**
     * Define a Meta Tag which identifies a Virtual Machine Failover Deployer
     */    
    public static final String VMFailoverDeployerTag = "mda.vfdeployer.id";
    
    public static final String FTManagerTag = "mda.ftmanager.id";
    
    public static final String MonitorTag = "mda.monitor.id";
    
    public static final String MDAHome = "JITCLOUDS_MDA_HOME";
    

    public static final Properties BaseOptions = new Properties();
    //public static final Properties HostOptions = new Properties();
    public static PrintStream out = System.out;
 
    //private static IConfiguration BaseConfiguration = null;
    public static CompositeConfiguration Configuration = null;
    //public static IConfiguration HostConfiguration = null;
    
    public static boolean ShellExecute = false;

    /* Context Tag*/
    public static final String Context = "Context";
        
    /* Failover Tags*/
    public static final String DCFailoverSynchPeriod = "DCFailoverSynchPeriod";
    
    /* Plan Tags */
    public static final String AllocationPlan = "AllocationPlan";
    public static final String ReplicationPlan = "ReplicationPlan";
    public static final String RecoveryPlan = "RecoveryPlan";
    
    /* Report Tags */
    public static final String EvaluationReport = "EvaluationReport";
    public static final String SynchronizationReport = "SynchronizationReport";
    public static final String ReplicationReport = "ReplicationReport";
    
    public static final String DCFailoverDeploymentReportList = "DCFailoverDeploymentReportList";
    
    /* Host Tags */
    public static final String HostEndPoint = "EndPoint";
    
    public static final String SLA = "SLA";
    
    
    public enum Contexts{
        CloudReplicationDeployment, 
        CloudRecovery, 
        CloudReconfiguration, 
        CloudFaultNotification, 
        CloudSynchronization, 
        CloudDeployment,
        DCReplication,
        DCRecovery,
        DCReconfiguration,
        DCFaultNotification,
        DCFailoverDeployment
    }
    
            
    protected static void loadBaseOptions() throws Exception{        
        
        BaseOptions.put(
           MDA.CloudTag,
           "cloud_0"
        );
        
        BaseOptions.put(
           MDA.CloudProvider,
           "br.ufba.lasid.jitclouds.mda.providers.CloudProvider"
        );

        BaseOptions.put(
           MDA.DCProvider,
           "br.ufba.lasid.jitclouds.mda.providers.DataCenterProvider"
        );

        BaseOptions.put(
           MDA.DCTag,
           "dc_0"
        );
        
        BaseOptions.put(
           MDA.ClusterProvider,
           "br.ufba.lasid.jitclouds.mda.providers.ClusterProvider"
        );

        BaseOptions.put(
           MDA.ClusterTag,
           "cluster_0"
        );

        BaseOptions.put(
           MDA.NodeProvider,
           "br.ufba.lasid.jitclouds.mda.providers.NodeProvider"
         );

        
        BaseOptions.put(
           MDA.NodeTag,
           "node0"
        );

        BaseOptions.put(
           MDA.HypervisorProvider,
           "br.ufba.lasid.jitclouds.mda.providers.HypervisorProvider"
         );
        
        BaseOptions.put(
           MDA.HypervisorTag,
           "xen"
        );                
        
        BaseOptions.put(
           MDA.VMProvider,
           "br.ufba.lasid.jitclouds.mda.providers.XenVMProvider"
         );

        BaseOptions.put(
           MDA.VMTag,
           "xen0"
        );                

        BaseOptions.put(
           MDA.VMFailoverDeployerProvider,
           "br.ufba.lasid.jitclouds.mda.providers.RemusBasedVMFailoverDeployerProvider"
         );

        BaseOptions.put(
           MDA.VMFailoverDeployerTag,
           "failover0"
        );                
        
        
        BaseOptions.put(
           MDA.MDAHome,
           System.getenv(MDAHome)
        );
                        
    }
    
    public static void startup() throws Exception{
        
        /**
         * Load default options (MDA.BaseOptions)
         */
        loadBaseOptions();
        
        Configuration = new CompositeConfiguration();
        
        Configuration.addConfiguration(
            new PropertiesConfiguration(getFullPath("etc/mda.base.properties")), true
        );
        
//        BaseConfiguration = 
//           new FileBasedConfiguration(
//                new PropertiesConfiguration(getFullPath("etc/mda.base.properties"))
//         );        
//        
        debug = Boolean.parseBoolean((String)Configuration.getString("mda.debug"));
        ShellExecute = Boolean.parseBoolean((String)Configuration.getString("mda.shell.execute"));
                        
        MDA.debug("Preparing to load the MDA Configuration files ...");
        
        List files = Configuration.getList("mda.config");
        
        for(Object file : files){
            
            String filename = getFullPath(file.toString());
            
            Configuration.addConfiguration(
                new PropertiesConfiguration(filename), true
            );
            
            MDA.debug("The properties file '" + filename + "' has been successfully loaded.");            
        }
        
        for(Object prop : BaseOptions.keySet()){
            
            String value = Configuration.getString((String)prop);
            
            if(value != null){
                BaseOptions.put(prop, value);
            }
        }
        
//        for(String s : BaseConfiguration.keySet()){
//            BaseOptions.put(s, BaseConfiguration.get(s));
//        }
//        
////        HostConfiguration = 
////           new FileBasedConfiguration(
////                new PropertiesConfiguration(getFullPath("etc/mda.nodes.properties"))
////         );        
////        
////        for(String s : HostConfiguration.keySet()){
////            HostOptions.put(s, HostConfiguration.get(s));
////        }
////        
        
        debug("MDA was initialized");
    }
    
    public static <T> T create(String providername) throws Exception{

        return (T)create(providername, new Properties(MDA.BaseOptions));
    }
    
    public static ICloud createCloud() throws Exception{
        return MDA.create(MDA.getProvider(MDA.CloudTag));
    }

    public static ICloud createCloud(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.CloudTag), options);
    }
    
    public static IDataCenter createDC() throws Exception{
        return MDA.create(MDA.getProvider(MDA.DCTag));
    }

    public static IDataCenter createDC(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.DCTag), options);
    }
    
    public static ICluster createCluster() throws Exception{
        return MDA.create(MDA.getProvider(MDA.ClusterTag));
    }

    public static ICluster createCluster(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.ClusterTag), options);
    }
    
    public static INode createNode() throws Exception{
        return MDA.create(MDA.getProvider(MDA.NodeTag));
    }

    public static INode createNode(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.NodeTag), options);
    }
    
    public static IHypervisor createHypervisor() throws Exception{
        return MDA.create(MDA.getProvider(MDA.HypervisorTag));
    }

    public static IHypervisor createHypervisor(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.HypervisorTag), options);
    }
    
    public static IVirtualMachine createVM() throws Exception{
        return MDA.create(MDA.getProvider(MDA.VMTag));
    }

    public static IVirtualMachine createVM(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.VMTag), options);
    }

    public static IVMFailoverDeployer createVMFailover() throws Exception{
        return MDA.create(MDA.getProvider(MDA.VMFailoverDeployerTag));
    }

    public static IVirtualMachine createVMFailover(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.VMFailoverDeployerTag), options);
    }

    public static JiTFTManagerAPI createFTManager() throws Exception{
        return MDA.create(MDA.getProvider(MDA.FTManagerTag));
    }
    
    public static JiTFTManagerAPI createFTManager(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.FTManagerTag), options);
    }
    
    public static JiTMonitorAPI createMonitor() throws Exception{
        return MDA.create(MDA.getProvider(MDA.MonitorTag));
    }
    
    public static JiTMonitorAPI createMonitor(Properties options) throws Exception{
        return MDA.create(MDA.getProvider(MDA.MonitorTag), options);
    }
    
    
    public static Class getProvider(String tag) throws Exception{
        String idTag = MDA.BaseOptions.getProperty(tag);
        String provider = MDA.Configuration.getString(idTag + ".provider");                
        return Class.forName(provider);
    }

    public static <T> T create(String providername, Properties options) throws Exception{

        Properties ioptions = new Properties(MDA.BaseOptions);
        ioptions.putAll(options);

        String provider = ioptions.getProperty(providername);

        Class _class = Class.forName(provider);

        return (T) create(_class, ioptions);
        
    }
    public static <T> T create(Class _class) throws Exception{
         return (T) create(_class, MDA.BaseOptions);
    }

    public static <T> T create(Class _class, Properties options) throws Exception{

        IProvider factory =  (IProvider) _class.newInstance();

        return (T) factory.create(options);
    }
    
    public static boolean debug = true;

    public static boolean isDebug() {
        return debug;
    }

    public static void setDebug(boolean debug) {
        MDA.debug = debug;
    }

    public synchronized static void debug(String txt){
        if(isDebug()) {
            out.println("[DEBUG]" + "[" + Calendar.getInstance().getTime() + "] " + txt);
        }
    }
    
    public static String getFullPath(String relativePath){
        return BaseOptions.getProperty(MDA.MDAHome) + "/" + relativePath;
    }
    
    public static <T> T get(String name) throws Exception{
        return null;
    }
    
}
